package com.carhailing.alipay.service.impl;

import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.AlipayTradeQueryModel;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradePagePayRequest;
import com.alipay.api.request.AlipayTradeQueryRequest;
import com.alipay.api.response.AlipayTradeQueryResponse;
import com.carhailing.alipay.config.AliPayResource;
import com.carhailing.alipay.mapper.IOrderPayInfoMapper;
import com.carhailing.alipay.service.IMyAlipayService;
import com.carhailing.alipay.service.IOrderTradeService;
import com.carhailing.common.domain.OrderPayInfo;
import com.carhailing.common.domain.OrderTrade;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.UnsupportedEncodingException;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @Author：老猫
 * @Project：Online-Taxi-Project
 * @Date：2024/7/26
 */
@Slf4j
@Service
public class MyAlipayServiceImpl implements IMyAlipayService {
   public static final String PAYMENT_ORDER_TOKEN = "orderToken";
   public static final int ORDERPAYINFO_STATUS = 0;
   @Autowired
   private AliPayResource aliPayResource;

   @Autowired
   private RedisTemplate<String, String> redisTemplate;

   @Autowired
   private IOrderTradeService iOrderTradeService;
   @Resource
   private IOrderPayInfoMapper iOrderPayInfoMapper;



   /**
    * 获取支付宝token,防止重复下单
    *
    * @return
    */
   @Override
   public String getOrderToken() {
      String uuid = UUID.randomUUID().toString().replace("-", "");
      redisTemplate.opsForValue().set(PAYMENT_ORDER_TOKEN, uuid, 5, TimeUnit.MINUTES);
      return uuid;
   }

   /**
    * @param orderTrade 订单信息
    * @param orderToken 下单的token
    * @return
    */
   @Override
   public void goUnderOrder(OrderTrade orderTrade, String orderToken) {
      if (StrUtil.isBlank(orderToken)) {
         throw new RuntimeException("参数不能为空");
      }
      // 获取token, 并判断token为空；
      String checkToken = redisTemplate.opsForValue().get(PAYMENT_ORDER_TOKEN);
      log.info("Check token: 【{}】" + checkToken);

      if (StrUtil.isBlank(checkToken)) {
         throw new RuntimeException("重复下单");
      }

      if (ObjectUtil.isEmpty(orderToken)) {
         throw new RuntimeException("参数不能为空");
      }
      // 本来要使用策略模式的 一个抽象类，定义四个方法；两三个继承类，在继承类写业务
      // 添加订单表，状态为待支付；  这里先直接调用支付宝
      orderTrade.setOrderStatusTrade(0);
      int i = iOrderTradeService.insertOrder(orderTrade);
   }

   /**
    * 支付宝支付
    *
    * @param orderNum 订单号
    * @param pattern  支付方式
    * @return
    */
   @Override
   public String goPay(String orderNum, String pattern) {
      // 非空效验
      if (StrUtil.isAllBlank(orderNum, pattern)) {
         throw new RuntimeException("参数不能为空");
      }
      // 防止重复支付  去数据库查 支付信息
//      iOrderPayInfoMapper.selectOrderPayInfoBy(ORDERPAYINFO_STATUS);

      // 效验；获取订单信息
      OrderTrade orderTrade = iOrderTradeService.getById(orderNum);
      if (null == orderTrade) {
         throw new RuntimeException("订单不存在");
      }
      // 给支付信息赋值
      OrderPayInfo orderPayInfo = new OrderPayInfo();

      // 添加支付记录表，避免重复支付
      int i = iOrderPayInfoMapper.insertOrderPayInfo(orderPayInfo);

      // 根据记录信息 ，调用支付宝支付接口
      callPaymentInterface(orderPayInfo.getPaymentSerialNumber(), orderPayInfo.getOrderNumber());

      return null;
   }

   /**
    * 支付成功后的支付宝异步通知,主动发给我信息交易结果
    */
   @Override
   public String paymentCallback(HttpServletRequest request) {
      log.info("支付成功后的异步通知");
      Map<String, String> newMap = new HashMap<>();
      // 获取支付宝POST过来反馈信息
      Map<String, String[]> res = request.getParameterMap();
      // 使用普通遍历也可以，他这里用来迭代器；获取所有key，然后遍历，并判断有下一个参数(key)
      for (Iterator<String> iter = res.keySet().iterator(); iter.hasNext(); ) {
         // 通过key取值
         String name = iter.next();
         String[] values = res.get(name);
         String valueStr = "";
         // 遍历数组，获取数组里面的值(只有一个参数)，
         for (int i = 0; i < values.length; i++) {
            // 有单个值时获取 values[0]。如果数组有多个值，结果是 valueStr += values[i] + "," ，
            valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ",";
         }
         //乱码解决，这段代码在出现乱码时使用
//       valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
         newMap.put(name, valueStr);
      }

      try {
         // 调用SDK验证签名;
         // 返回的参数， 公钥，编码格式 utf8, 加签验签方式 rsa
         boolean signVerified = AlipaySignature.rsaCheckV1(newMap, aliPayResource.getAlipayPublicKey(), aliPayResource.getCharset(), aliPayResource.getSignType());
         // 验签失败,返回结果
         if (!signVerified) {
            log.info("验签失败, 时间: {}", LocalDateTime.now());
            throw new RuntimeException("验签失败");
         }
         // 支付流水号
         String outTradeNo = new String(request.getParameter("out_trade_no").getBytes("ISO-8859-1"), "UTF-8");
         // 支付宝交易号
         String tradeNo = new String(request.getParameter("trade_no").getBytes("ISO-8859-1"), "UTF-8");
         // 交易状态
         String tradeStatus = new String(request.getParameter("trade_status").getBytes("ISO-8859-1"), "UTF-8");
         // 付款金额
         String totalAmount = new String(request.getParameter("total_amount").getBytes("ISO-8859-1"), "UTF-8");

         log.info("************* 支付成功(支付宝异步通知) - 时间: {} *************", LocalDateTime.now());
         log.info("* 订单号: {}", outTradeNo);
         log.info("* 支付宝交易号: {}", tradeNo);
         log.info("* 实付金额: {}", tradeStatus);
         log.info("* 交易状态: {}", totalAmount);
         log.info("*****************************************************************************");
         // 判断交易失败
         if (!"TRADE_SUCCESS".equals(tradeStatus)) {
            throw new RuntimeException("交易失败");
         }
         // 修改流水表状态
         System.out.println("修改流水表状态");
         //                String merchantReturnUrl = paymentOrderService.updateOrderPaid(out_trade_no, CurrencyUtils.getYuan2Fen(total_amount));
         //                notifyFoodieShop(out_trade_no,merchantReturnUrl);
      } catch (AlipayApiException e) {
         throw new RuntimeException(e);
      } catch (UnsupportedEncodingException e) {
         throw new RuntimeException(e);
      }
      return "回调成功";
   }

   /**
    * 主动调查询支付结果
    */
   @Override
   public String queryAlipayResult(String num, String alipayNum) {
      // 初始化SDK
      AlipayClient alipayClient = null;
      try {
         alipayClient = new DefaultAlipayClient(aliPayResource.getAlipayConfig());
      } catch (AlipayApiException e) {
         throw new RuntimeException(e);
      }

      List<String> queryOptions = new ArrayList<>();
      queryOptions.add("trade_settle_info");
      AlipayTradeQueryModel model = new AlipayTradeQueryModel();
      // 设置订单支付时传入的商户订单号
      model.setOutTradeNo(num);
      // 设置支付宝交易号
      model.setTradeNo(alipayNum);
      // 设置查询选项
      model.setQueryOptions(queryOptions);
      // 构造请求参数以调用接口
      AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
      request.setBizModel(model);

      AlipayTradeQueryResponse response = null;
      try {
         response = alipayClient.execute(request);
      } catch (AlipayApiException e) {
         throw new RuntimeException(e);
      }

      System.out.println(response.getBody());

      if (!response.isSuccess()) {
         // sdk版本是"4.38.0.ALL"及以上,可以参考下面的示例获取诊断链接
         // String diagnosisUrl = DiagnosisUtils.getDiagnosisUrl(response);
         // System.out.println(diagnosisUrl);
         return "调用失败";
      }
      return "调用成功";
   }

   /**
    * 拼接用户订单参数，添加流水表，发起请求，返回支付宝路径，前往支付宝支付页面，进行支付。
    *
    * @param paymentSerialNumber 支付流水号
    * @param orderNumber         订单名称
    * @return
    */
   private String callPaymentInterface(String paymentSerialNumber, String orderNumber) {
      //请求
      String alipayForm = "";
      try {
         //获得初始化的AlipayClient
         AlipayClient alipayClient = new DefaultAlipayClient(aliPayResource.getAlipayConfig());

         JSONObject bizContent = new JSONObject();
         // 必填：支付流水号, 唯一性,
         bizContent.put("out_trade_no", paymentSerialNumber);
         // 必填：付款金额 单位元：
         bizContent.put("total_amount", "1000");
         // 订单名称, 必填
         bizContent.put("subject", "胡金水-付款用户[" + orderNumber + "]");
         // 商品描述, 可空
         bizContent.put("body", "商品描述");
         bizContent.put("product_code", "FAST_INSTANT_TRADE_PAY");
         // 该笔订单允许的最晚付款时间，逾期将关闭交易。取值范围：1m～15d。m-分钟，h-小时，d-天，1c-当天（1c-当天的情况下，无论交易何时创建，都在0点关闭）。
         // 该参数数值不接受小数点， 如 1.5h，可转换为 90m。
         bizContent.put("timeout_express", "30m");

         // 支付宝交易支付请求
         AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest();
         // 回调和异步请求路径
         alipayRequest.setReturnUrl(aliPayResource.getReturnUrl());
         alipayRequest.setNotifyUrl(aliPayResource.getNotifyUrl());
         alipayRequest.setBizContent(bizContent.toString());

         alipayForm = alipayClient.pageExecute(alipayRequest).getBody();

         System.out.println(alipayForm);
         log.info("支付宝支付 - 前往支付页面, alipayForm: \n{}", alipayForm);
      } catch (AlipayApiException e) {
         throw new RuntimeException(e);
      }
      return alipayForm;
   }


}
